
package org.sopt.dev.ImgKnight.agent.URL.Util;

import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.StringTokenizer;

public class UrlUtil {
	
	private static final Set<Character> UNRESERVED_CHARACTERS = new HashSet<Character>();
	
	static {
		for(char c = 'a'; c <= 'z'; c++)
			UNRESERVED_CHARACTERS.add(new Character(c));
		for(char c = 'A'; c <= 'Z'; c++)
	        UNRESERVED_CHARACTERS.add(new Character(c));
		for(char c = '0'; c <= '9'; c++)
	        UNRESERVED_CHARACTERS.add(new Character(c));
		UNRESERVED_CHARACTERS.add(new Character('-'));
	    UNRESERVED_CHARACTERS.add(new Character('.'));
	    UNRESERVED_CHARACTERS.add(new Character('_'));
	    UNRESERVED_CHARACTERS.add(new Character('~'));
	    UNRESERVED_CHARACTERS.add(new Character('!'));
	    UNRESERVED_CHARACTERS.add(new Character('*'));
	    UNRESERVED_CHARACTERS.add(new Character('\''));
	    UNRESERVED_CHARACTERS.add(new Character('('));
	    UNRESERVED_CHARACTERS.add(new Character(')'));
	}
	
	final static String[] HEX_VALUE = {
	    "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
	    "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
	    "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
	    "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
	    "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
	    "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
	    "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
	    "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
	    "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
	    "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
	    "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
	    "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
	    "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
	    "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
	    "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
	    "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
	    "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
	    "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
	    "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
	    "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
	    "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
	    "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
	    "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
	    "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
	    "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
	    "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
	    "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
	    "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
	    "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
	    "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
	    "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
	    "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
	};
	  
	public static boolean isRelative(String url) {
		assert(url != null);
		try {
			URI uri = new URI(url);
			String scheme = uri.getScheme();
			String host = uri.getHost();
			String path = uri.getPath();
			if( scheme == null && host == null && path != null ) {
				return true;
			}
		} catch (URISyntaxException e) { }
		return false;
	}
	
	public static String resolve(String base, String ref) {
		if( !isFileSpecified(base) && !base.endsWith("/") )
			base += "/";
		try {
			URI uri = new URI(base);
			return uri.resolve(ref).toString();
		} catch (URISyntaxException e) { }
		return null;
	}
	
	public static int getPathDepth(URL url) {
		assert(url != null);
		int depth = -1;
		String path = url.getPath();
		if( path == null )
			return depth;
		if( isFileSpecified(url) && !path.endsWith("/") )
			path += "/";
		int pos = path.indexOf('/');
		while( pos != -1 ) {
			if( pos > 0 )
				depth++;
			pos = path.indexOf('/', pos+1);
		}
		return depth;
	}
	
	public static int getPathDepth(String path) {
		assert(path != null);
		try {
			URL url = new URL(path);
			return getPathDepth(url);
		} catch (Exception e) {
		}
		return -1;
	}
	
	public static boolean isFileSpecified(URL url) {
		assert(url != null);
		String path = url.getPath();
		int posLastSlash = path.lastIndexOf('/');
		int posLastDot   = path.lastIndexOf('.');
		return (posLastDot > posLastSlash) ? true : false;
	}
	
	public static boolean isFileSpecified(String path) {
		assert(path != null);
		try {
			URL url = new URL(path);
			return isFileSpecified(url);
		} catch (Exception e) {
		}
		return false;
	}	
	
	public static String[] getFolderNames(URL url) {
		assert(url != null);
		try {
			ArrayList<String> al = new ArrayList<String>();
			URL nomalizeURL = new URL(normalize(url.toString(), false, false));
			String path = nomalizeURL.getPath();
			if( isFileSpecified(nomalizeURL) ) {
				path = getResourcePath(path);
			}
			StringTokenizer st = new StringTokenizer(path, "/");
			while( st.hasMoreTokens() ) {
				al.add(st.nextToken());
			}
			return al.toArray(new String[al.size()]);
		} catch (MalformedURLException e) {}
		return null;
	}
	
	public static String getFimeName(URL url) {
		assert(url != null);
		return url.getPath().substring(getResourcePath(url.getPath()).length());
	}
	
	public static String getResourcePath(String url) {
		assert(url != null);
		int pos = url.lastIndexOf("/");
		return url.substring(0, pos+1);
	}
	
	public static String getRobotsTxtPath(URL url) {
		assert(url != null);
		return getHostURL(url).toString() + "/robots.txt";
	}
	
	public static URL getHostURL(URL url) {
		assert(url != null);
		try {
			URL host = new URL(url.getProtocol(), url.getHost(), url.getPort(), "");
			return host;
		} catch (MalformedURLException e) {
			/* Shouldn't happen */
		}
		return null;
	}
	
	public static String getHostURL(String url) {
		assert(url != null);
		try {
			URL tmp = new URL(url);
			return getHostURL(tmp).toString();
		} catch (MalformedURLException e) {
			/* Shouldn't happen */
		}
		return null;
	}
	
	public static String normalize(String toNomalizeURL, boolean removeQuery, boolean removeFragment) 
		throws  MalformedURLException {
		assert(toNomalizeURL != null);
		URL url = new URL(toNomalizeURL);
		String protocol = url.getProtocol().toLowerCase();
		String host = url.getHost().toLowerCase();
		String path = encodeURL(url.getPath());
		String query = encodeURL(url.getQuery());
		String fragment = encodeURL(url.getRef());
		int port = (url.getPort() == url.getDefaultPort()) ? -1 : url.getPort();
		
		if( !isFileSpecified(path) && query == null ) {
			if( path.endsWith("/") )
				path = path.substring(0, path.lastIndexOf('/'));
		}
		
		String file = path;
		if( query != null && !removeQuery )
			file = file + "?" + query;
		if( fragment != null && !removeFragment )
			file = file + "#" + fragment;
		return new URL(protocol, host, port, file).toString();
	}
	
	public static String encodeURL(String url) {
		if( url == null )
			return null;
		int len = url.length();
		StringBuffer strBuffer = new StringBuffer(len);
		for(int i=0; i<len; i++) {
			char curr = url.charAt(i);
			if( curr == '%' && i < len-2 ) {
				String code = url.substring(i, i+3).toUpperCase();
				try {
					char chr = URLDecoder.decode(code, "UTF-8").charAt(0);
					if( UNRESERVED_CHARACTERS.contains(new Character(chr)) )
						strBuffer.append(chr);
					else
						strBuffer.append(code);
				} catch (Exception e) {
					strBuffer.append(code);
				}
				i += 2;
			}
			else {
				if( UNRESERVED_CHARACTERS.contains(new Character(curr)) )
					strBuffer.append(curr);
				else if( curr == '/' )
					strBuffer.append('/');
				else if( curr == '=' )
					strBuffer.append('=');
				else if( curr <= 0x007F )
					strBuffer.append(HEX_VALUE[curr]);
				else if( curr <= 0x07FF ) {
					strBuffer.append(HEX_VALUE[0xC0 | (curr >> 6)]);
					strBuffer.append(HEX_VALUE[0x80 | (curr & 0x3F)]);
				} 
				else {
					strBuffer.append(HEX_VALUE[0xE0 | (curr >> 12)]);
					strBuffer.append(HEX_VALUE[0x80 | ((curr >> 6) & 0x3F)]);
					strBuffer.append(HEX_VALUE[0x80 | (curr & 0x3F)]);
				}
			}
		}
		return strBuffer.toString();
	}
	
}
